﻿using System.Collections;
using TrinityEngine.SceneManagement;

namespace TrinityEngine
{
    public abstract class GameObject
    {
        public Transform transform = new Transform();

        private Scene _scene = null;

        private List<Component> _components = new List<Component>();

        public bool activeSelf { get; private set; } = true;

        public bool isDestroyed { get; private set; } = false;

        public bool isUI = false;

        private List<IEnumerator> _coroutineList = new List<IEnumerator>();

        protected virtual void FixedUpdate() { }

        protected virtual void Update() { }

        protected virtual void OnGUI() { }

        protected virtual void Render() { }

        protected virtual void OnEnable() { }

        protected virtual void OnDisable() { }

        protected virtual void OnDestroy() { }

        public void AddComponent<T>(T component) where T : Component
        {
            component.transform = transform;
            component.CallAwake();
            _components.Add(component);
        }

        public void SetScene(Scene scene)
        {
            _scene = scene;
        }

        public void CallFixedUpdate()
        {
            if (!activeSelf)
            {
                return;
            }

            foreach (var component in _components)
            {
                if (component is PhysicsBody)
                {
                    (component as PhysicsBody).CallFixedUpdate();
                }
            }

            FixedUpdate();
        }

        public void CallUpdate()
        {
            if (!activeSelf)
            {
                return;
            }

            foreach (var component in _components)
            {
                component.CallUpdate();
            }

            Update();
            UpdateCoroutines();
        }

        public void CallRender()
        {
            if (!activeSelf)
            {
                return;
            }

            OnGUI();

            foreach (var component in _components)
            {
                if (component is Renderer)
                {
                    (component as Renderer).CallRender();
                }
            }

            Render();
        }

        public void SetActive(bool active)
        {
            activeSelf = active;

            foreach (var component in _components)
            {
                component.enabled = active;
            }

            if (activeSelf)
            {
                OnEnable();
            }
            else
            {
                StopAllCoroutines();
                OnDisable();
            }
        }

        public void UpdateCoroutines()
        {
            for (int i = 0; i < _coroutineList.Count; i++)
            {
                bool canMoveNext = true;
                if (_coroutineList[i].Current is WaitForSeconds)
                {
                    canMoveNext = (_coroutineList[i].Current as WaitForSeconds).DealWithYield();
                }

                if (!canMoveNext)
                {
                    continue;
                }

                if (!_coroutineList[i].MoveNext())
                {
                    _coroutineList.RemoveAt(i--);
                }
            }
        }

        public void StartCoroutine(IEnumerator coroutine)
        {
            if (coroutine == null)
            {
                return;
            }

            _coroutineList.Add(coroutine);
        }

        public void StopAllCoroutines()
        {
            _coroutineList.Clear();
        }

        public void Destroy()
        {
            isDestroyed = true;
        }

        public void DestroyImmediate()
        {
            activeSelf = false;

            foreach (var component in _components)
            {
                component.CallOnDisable();
                component.CallOnDestroy();
            }

            StopAllCoroutines();
            OnDisable();
            OnDestroy();
        }
    }
}
